#include "stdafx.h"
#include "net/HTTPServerConnection.hpp"
#include "net/HTTPServerSession.hpp"
#include "net/HTTPServerRequestImpl.hpp"
#include "net/HTTPServerResponseImpl.hpp"
#include "event/Delegate.hpp"
using JHCPP::event::delegate;

NET_NAMESPACE_BEGIN

CHTTPServerConnection::CHTTPServerConnection(const CStreamSocket& socket, CHTTPServerParams::Ptr pParams, CHTTPRequestHandlerFactory::Ptr pFactory)
	: CTCPServerConnection(socket),
	m_pParams(pParams),
	m_pFactory(pFactory),
	m_stopped(false)
{
	jh_check_ptr (pFactory);

	m_pFactory->serverStopped += delegate(this, &CHTTPServerConnection::onServerStopped);
}

CHTTPServerConnection::~CHTTPServerConnection()
{
	try
	{
		m_pFactory->serverStopped -= delegate(this, &CHTTPServerConnection::onServerStopped);
	}
	catch (...)
	{
		jh_unexpected();
	}
}

void CHTTPServerConnection::run()
{
	std::string server = m_pParams->getSoftwareVersion();
	CHTTPServerSession session(socket(), m_pParams);
	while (!m_stopped && session.hasMoreRequests())
	{
		try
		{
			CMutex::ScopedLock lock(m_mutex);
			if (!m_stopped)
			{
				CHTTPServerResponseImpl response(session);
				CHTTPServerRequestImpl request(response, session, m_pParams);

				CTimeStamp now;
				response.setDate(now);
				response.setVersion(request.getVersion());
				response.setKeepAlive(m_pParams->getKeepAlive() && request.getKeepAlive() && session.canKeepAlive());
				if (!server.empty())
					response.set("Server", server);
				try
				{
					std::auto_ptr<CHTTPRequestHandler> pHandler(m_pFactory->createRequestHandler(request));
					if (pHandler.get())
					{
						if (request.expectContinue())
							response.sendContinue();

						pHandler->handleRequest(request, response);
						session.setKeepAlive(m_pParams->getKeepAlive() && response.getKeepAlive() && session.canKeepAlive());
					}
					else sendErrorResponse(session, CHTTPResponse::HTTP_NOT_IMPLEMENTED);
				}
				catch (CException&)
				{
					if (!response.sent())
					{
						try
						{
							sendErrorResponse(session, CHTTPResponse::HTTP_INTERNAL_SERVER_ERROR);
						}
						catch (...)
						{
						}
					}
					throw;
				}
			}
		}
		catch (NoMessageException&)
		{
			break;
		}
		catch (MessageException&)
		{
			sendErrorResponse(session, CHTTPResponse::HTTP_BAD_REQUEST);
		}
		catch (CException&)
		{
			if (session.networkException())
			{
				session.networkException()->rethrow();
			}
			else throw;
		}
	}
}

void CHTTPServerConnection::sendErrorResponse(CHTTPServerSession& session, CHTTPResponse::HTTPStatus status)
{
	CHTTPServerResponseImpl response(session);
	response.setVersion(CHTTPMessage::HTTP_1_1);
	response.setStatusAndReason(status);
	response.setKeepAlive(false);
	response.send();
	session.setKeepAlive(false);
}

void CHTTPServerConnection::onServerStopped(const bool& abortCurrent)
{
	m_stopped = true;
	if (abortCurrent)
	{
		try
		{
			// Note: On Windows, select() will not return if one of its socket is being
			// shut down. Therefore we have to call close(), which works better.
			// On other platforms, we do the more graceful thing.
		#if defined(_WIN32)
			socket().close();
		#else
			socket().shutdown();
		#endif
		}
		catch (...)
		{
		}
	}
	else
	{
		CMutex::ScopedLock lock(m_mutex);

		try
		{
		#if defined(_WIN32)
			socket().close();
		#else
			socket().shutdown();
		#endif
		}
		catch (...)
		{
		}
	}
}

NET_NAMESPACE_END
